package Buildings.office;
import Interfaces.*;
import Errors.*;
import java.util.Iterator;
public class OfficeFloor implements Floor, java.io.Serializable, Cloneable{
    private note mainOffice; //голова динамической структуры офисов
    private class note implements java.io.Serializable{
        public Space s;
        public boolean last=false;
        private note nextNote=null;
        note(Space s){
            this.s=s;
        }
        note(Space s,note next){
            this.s=s;
            nextNote=next;
        }
    }
    private class myIterator implements Iterator{
        private note mainOfficei=new note(null,mainOffice);
        public boolean hasNext(){
            boolean answer=false;
            if(mainOfficei.last==false)answer=true;
            return answer;
        }
        public Space next(){
            if(mainOfficei.last!=true){
            mainOfficei=mainOfficei.nextNote;
            return mainOfficei.s;
            }else return null;
        }
        public void remove(){
            throw new UnsupportedOperationException();
        }
    }
    
    private note getOffice(int n){
        try{
            if(n<0){throw new SpaceIndexOutOfBoundsException();}
            if(n>=0){
                //костыль для нумерации с нуля
                n++;
                //конец костыля
                int m=getHowMuchSpaceInFloor();
                n%=m;
                if(n==0)n=m;
                note of=mainOffice;
                for(int i=1;i<n;i++){
                    of=of.nextNote;
                }
                return of;
            }else return null;
        }catch(SpaceIndexOutOfBoundsException e){
            System.out.println(e);
            return null;
        }
    }
    private void addOffice(int n, note newNote){
        try{
            if(n<0){throw new SpaceIndexOutOfBoundsException();}
            if(n>=0){
                //костыль для нумерации с нуля
                n++;
                //конец костыля
                int m=getHowMuchSpaceInFloor();
                n%=m;
                if(n==0)n=m;
                if(n==1){
                   getOffice(m-1).nextNote=newNote;
                   newNote.nextNote=mainOffice;
                   mainOffice=newNote;
                   return;
                }
                if(n==m){//добавление в хвост
                    note s=getOffice(m-1);//пока последний в списке
                    s.nextNote=newNote;
                    s.last=false;
                    s.nextNote.last=true;
                    s.nextNote.nextNote=mainOffice; 
                    return;
                }
                if(n>1 && n<m){//добавление в тело
                    newNote.nextNote=getOffice(n);
                    getOffice(n-1).nextNote=newNote;
                }    
            }
        }catch(SpaceIndexOutOfBoundsException e){
            System.out.println(e);
        }
    }
    private void delOffice(int n){
        try{
        if(n<0){throw new SpaceIndexOutOfBoundsException();}
        if(n>=0){
            //костыль для нумерации с нуля
                n++;
            //конец костыля
            int m=getHowMuchSpaceInFloor();
            n%=m;
            if(n==0)n=m;
            if(n==1){//удаляем голову //если осталась только голова, ее не удаляем
                getOffice(m-1).nextNote=mainOffice.nextNote;
                mainOffice=mainOffice.nextNote;
                return;
            }
            if(n==m){//удаляем хвост
                getOffice(m-2).last=true;
                getOffice(m-1).nextNote=mainOffice;//уже новый хвост
                return;
            }
            if(n>1 && n<m){//удаление в теле
                getOffice(n-1).nextNote=getOffice(n+1);
            }
        }
        }catch(SpaceIndexOutOfBoundsException e){
           System.out.println(e);
        }
    }
    private void setOfficePrivate(int n, note s){
         try{
            if(n<0){throw new SpaceIndexOutOfBoundsException();}
            getOffice(n).s=s.s;
         }catch(SpaceIndexOutOfBoundsException e){
            System.out.println(e);
         }
    }
    
    @Override
    public java.util.Iterator iterator(){
        return new myIterator();
    }
    @Override
    public int getHowMuchSpaceInFloor(){
        int n=0;
        try{
            note of=mainOffice;
            boolean exit=false;
            while(exit==false){
                if(of.last==true)exit=true;
                of=of.nextNote;
                n++;
                if(of==mainOffice && exit!=true)throw new SpaceIndexOutOfBoundsException();
            }
        }catch(SpaceIndexOutOfBoundsException e){
            System.out.println(e);
            n=0;
        }
        return n;
    }
    @Override
    public double getHowMuchAreaInFloor(){
        note of=mainOffice;
        double s=0;
        for(int i=0;i<getHowMuchSpaceInFloor();i++){
            s+=of.s.getHowMuchArea();
            of=of.nextNote;
        }
        return s;
    }
    @Override
    public int getHowMuchRoomsInFloor(){
        note of=mainOffice;
        int n=0;
        for(int i=0;i<getHowMuchSpaceInFloor();i++){
            n+=of.s.getHowMuchRooms();
            of=of.nextNote;
        }
        return n;
    }
    @Override
    public Space[] getMassSpacesInFloor(){//отдаем ссылку на действующие помещения
        Space mass[];
        note of=mainOffice;
        int n=getHowMuchSpaceInFloor();
        mass=new Space[n];
        for(int i=0;i<n;i++){
            mass[i]=of.s;
            if(i==(n-1))break;//т.к. не нужно брать опять голову
            of=of.nextNote;
        }
        return mass;        
    }
    @Override
    public Space getSpaceInFloor(int n){
        return getOffice(n).s;
    }
    @Override
    public void setSpaceInFloor(int n,Space newOffice){
          note s=new note((Space)newOffice.clone());
          setOfficePrivate(n,s);
    }
    @Override
    public void addSpaceInFloor(int n, Space newOffice){
        note s=new note((Space)newOffice.clone());
        addOffice(n,s);
    }
    @Override
    public void delSpaceInFloor(int n){
        delOffice(n);
    }
    @Override
    public Space getBestSpace(){
         note of=mainOffice;
         Space best=null;
         double s=0;
         try{
         for(int i=0;i<getHowMuchSpaceInFloor();i++){
            if(s<of.s.getHowMuchArea()){
                best=of.s;
                s=of.s.getHowMuchArea();
            }
            of=of.nextNote;
            if(of==mainOffice &&s==0)throw new SpaceIndexOutOfBoundsException();
         }
         }catch(SpaceIndexOutOfBoundsException e){
             System.out.println(e);
         }
         return best;
    }

    public String toString(){
        StringBuilder s=new StringBuilder();
        s.append(" этаж (офисный) \n");
        for(int i=0;i<getHowMuchSpaceInFloor();i++){
            s.append((getSpaceInFloor(i).toString()));
        }
        s.append("\n");
        return s.toString();
    }
    public boolean equals(Object o){
        boolean answer=false;
        if(o.getClass()==this.getClass()){
            if(((OfficeFloor)o).getHowMuchSpaceInFloor()==this.getHowMuchSpaceInFloor()){
                boolean key=true;
                for(int i=0;i<getHowMuchSpaceInFloor();i++){
                    if(getSpaceInFloor(i).equals(((OfficeFloor)o).getSpaceInFloor(i))==false){
                        key=false;
                        break;
                    }       
                }
                if(key==true)answer=true;
            }
        }
        return answer;
    }
    public int hashCode(){
        int hash;
        hash=(Integer.bitCount(this.getHowMuchSpaceInFloor()));
        for(int i=0;i<this.getHowMuchSpaceInFloor();i++){
            hash=(int)(hash ^ this.getSpaceInFloor(i).hashCode());
        }
        return hash;
    }
    public Object clone(){
        Space forClone[];
        note of=mainOffice;
        int n=getHowMuchSpaceInFloor();
        forClone=new Space[n];
        for(int i=0;i<n;i++){
            forClone[i]=(Space)of.s.clone();
            if(i==(n-1))break;//т.к. не нужно брать опять голову
            of=of.nextNote;
        }
        return new OfficeFloor(forClone);
    }
    
    public OfficeFloor(int n){
        try{
        if(n>0){
            if(n==1){
                mainOffice=new note(new Office());
                mainOffice.last=true;
                mainOffice.nextNote=mainOffice;
                return;
            }
            mainOffice=new note(new Office());
            note of=mainOffice;
            for(int i=1;i<n;i++){
                of.nextNote=new note(new Office());
                if(i==(n-1)){
                    of.nextNote.last=true;
                    of.nextNote.nextNote=mainOffice;
                    break;
                }
                of=of.nextNote;
            }
        }else throw new SpaceIndexOutOfBoundsException();
        }catch(SpaceIndexOutOfBoundsException e){
            System.out.println(e);
        }
        
    }
    public OfficeFloor(Space ... mass){//используем массив
        try{
        int n=mass.length;
        mainOffice=new note((Space)mass[0]);
        note of=mainOffice;
        if(n==0)throw new SpaceIndexOutOfBoundsException();
        if(n==1){
                mainOffice.last=true;
                mainOffice.nextNote=mainOffice;
                return;
        }
        for(int j=1;j<n;j++){
            of.nextNote= new note((Space)mass[j]);
            if(j==(n-1)){
                of.nextNote.last=true;
                of.nextNote.nextNote=mainOffice;
                break;
            }
            of=of.nextNote;
        }
        }catch(SpaceIndexOutOfBoundsException e){
            System.out.println(e);
        }
    }
}
